/**
 * @file        gtd.hpp
 * @brief       Defines specific methods related with todo items.
 *
 * @author      Emre Turkay <emreturkay at gmail>
 * @date        May.31.2008
 */

#ifndef _GTD_HPP_
#define _GTD_HPP_

#include <stdexcept>
#include <string>
#include <list>
#include <iterator>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <vector>

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/optional.hpp>
#include <boost/none.hpp>

#include "projects.hpp"

namespace gtd
{

    /*
     * Exceptions.
     */

    struct no_item: std::runtime_error
    {
        no_item(): std::runtime_error("No more items") { }
    };

    struct unknown_project: std::runtime_error
    {
        unknown_project(const std::string& name)
            : std::runtime_error(std::string("Unknown project: ") + name)
        {
        }
    };

    struct done_item
    {
        done_item() { }

        done_item(const std::string& l)
            : time_(boost::posix_time::second_clock::local_time()),
              line_(l)
        {
        }

        done_item(boost::posix_time::ptime t, const std::string& line)
            : time_(t), line_(line)
        {
        }

        bool operator<(const done_item& i) const
        {
            if (time_ < i.time_)
                return true;
            if (time_ > i.time_)
                return false;
            return line_ > i.line_;
        }

        void write(std::ostream& os) const
        {
            boost::posix_time::time_facet* facet(
                new boost::posix_time::time_facet("[%a %Y-%b-%d %H:%M]"));
            os.imbue(std::locale(os.getloc(), facet));
            os << time_ << " " << line_ << std::endl;
        }

        void read(std::istream& is)
        {
            boost::posix_time::time_input_facet* facet(
                new boost::posix_time::time_input_facet("[%a %Y-%b-%d %H:%M]"));
            is.imbue(std::locale(is.getloc(), facet));

            is >> time_;
            std::getline(is, line_);
            while (isspace(line_[0]))
                line_.erase(line_.begin());
        }

        const boost::posix_time::ptime& time() const
        {
            return time_;
        }

        const std::string& line() const
        {
            return line_;
        }

    private:
        boost::posix_time::ptime time_;
        std::string line_;
    };

    struct gtd
    {
        typedef size_t project_id;
        typedef std::map<project_id, project> projects;

        gtd();

        void save_done(std::ostream& os);

        project& get_project(project_id index);

        const project& get_project(project_id index) const;

        void done(project_id index);

        void cancel(project_id index);

        void defer(project_id index);

        void append(const std::string& value);

        void prepend(const std::string& value);

        void move_back(project_id index);

        bool empty() const;

        std::list<project_id> project_ids() const;

        std::list<project_id> project_ids(const std::string& ctx) const;

        const boost::optional<std::string>& project_name(project_id id) const;

        std::list<project::action_id> items(project_id id) const;

        const action& item(project_id id, project::action_id index) const;

        size_t item_count(project_id id) const;

        boost::optional<project_id> find_project(const std::string& name) const;

        void load_done(std::istream& is);

        const std::set<done_item>& done_items() const;

        std::set<done_item> done_items(boost::gregorian::date_duration d) const;

    private:
        project_id create_project(boost::optional<std::string> name);

    private:
        projects projects_;
        std::list<project_id> list_;
        std::set<done_item> done_items_;
    };

} // namespace gtd

#endif // _GTD_HPP_
